#ifndef ECCOPERATOR_H
#define ECCOPERATOR_H

#include "Deleter.h"
#include "config.h"
#include <cstring>
#include <fstream>
#include <iostream>
#include <memory>
#include <openssl/bn.h>
#include <openssl/ec.h>
#include <openssl/err.h>
#include <openssl/hmac.h>
#include <openssl/obj_mac.h>
#include <vector>

constexpr int salt_length = 16;
constexpr int salt_hash_length = 32;

unsigned char* generate_salt(size_t length);
std::string base64_encode(const unsigned char* data, size_t length);
std::vector<unsigned char> xorData(const std::vector<unsigned char>& data1,
                                   const std::vector<unsigned char>& data2);
std::vector<unsigned char> padStringWithZeros(const std::vector<unsigned char>& input,
                                              size_t target_length);
std::vector<unsigned char> concatenate(const std::vector<unsigned char>& a,
                                       const std::vector<unsigned char>& b);
std::vector<unsigned char> concatenate(const std::string& a, const std::string& b);

enum Model { ONLYGETPRIKEY, ONLYGETPUBKEY, GETBOTH };

typedef struct ordinary_value_initialization_parameter
{
    std::shared_ptr<EC_POINT> Gamma_i_ptr;
    std::shared_ptr<BIGNUM> O_i_ptr; //这是O_i
    std::shared_ptr<BIGNUM> remainder_ptr;
} ordinary_value_initialization_parameter;

class ECCOperator
{
public:
    ECCOperator();
    ~ECCOperator();

    BIGNUM* getPrime() { return q_; }
    BIGNUM* getOrder() { return n_; }
    BIGNUM* getXCoordinate() { return XP_; }
    BIGNUM* getYCoordinate() { return YP_; }
    BIGNUM* getCofactor() { return h_; }
    BIGNUM* geta() { return a_; }
    BIGNUM* getb() { return b_; }
    BN_CTX* getCtx() { return ctx_; }
    EC_GROUP* getGroup() { return curveAttributes_; }

    std::shared_ptr<BIGNUM> GeneratePriKey();
    EC_POINT* getBasePoint() { return P_; }
    std::shared_ptr<EC_POINT> GeneratePubKey(BIGNUM* PriKey);
    int BN_ECKey_Conv_EC_EVPKey(BIGNUM* k_priv,
                                EC_POINT* k_pub,
                                EC_GROUP* group,
                                EVP_PKEY** out_evp_pkey);
    int ECPubKey_Conv_EVPPubKey(EC_POINT* k_pub, EC_GROUP* group, EVP_PKEY** out_evp_pubkey);
    int BN_Convert_EVPPriKey(BIGNUM* k_pri, EC_GROUP* group, EVP_PKEY** out_evp_prikey);
    BIGNUM* EVPPriKey_Convert_BN(EVP_PKEY* pkey); //这是从EVP_PKEY的私钥中获取BIGNUM的公钥
    EC_POINT* EVP_PKEY_PubKey_Convert_EC(EVP_PKEY* pkey);
    std::shared_ptr<unsigned char> EC_POINT2Char(EC_POINT* point, point_conversion_form_t form);
    std::string EC_POINT_To_String(const EC_POINT* point);
    std::string ecPointToBinaryString(const EC_POINT* point);

    EC_POINT* ecBinaryStringToEcPoint(const std::string& point);
    BIGNUM* bnBinaryStringToBn(const std::string& bn_str);
    BIGNUM* bnBinaryCharToBn(const std::vector<unsigned char>& bn_bin_vector);
    std::string bnToBinarryString(const BIGNUM* bn);
    std::string bnTohexString(const BIGNUM* bn);
    int deriveSymmetricKey(const EC_GROUP* group,
                           const EC_POINT* pub_key,
                           BIGNUM* priv_key,
                           const unsigned char* salt,
                           unsigned char* out_key);

    //写一个将EVP_PKEY转换为DER格式的函数
    std::vector<unsigned char> EVP_PKEY_PublicKey_Convert_TO_DER(EVP_PKEY* pkey);
    //对盐值进行哈希

    //下面6个函数是我们对椭圆曲线运算的进一步封装
    std::shared_ptr<EC_POINT> ec_point_mul(const std::shared_ptr<BIGNUM>& bn,
                                           const std::shared_ptr<EC_POINT>& ec_point);
    std::shared_ptr<EC_POINT> ec_point_mul(const std::shared_ptr<BIGNUM>& bn);
    std::shared_ptr<EC_POINT> ec_point_add(const std::shared_ptr<EC_POINT>& a,
                                           const std::shared_ptr<EC_POINT>& b);
    std::shared_ptr<BIGNUM> bn_add(const std::shared_ptr<BIGNUM>& a,
                                   const std::shared_ptr<BIGNUM>& b);
    std::shared_ptr<BIGNUM> bn_mul(const std::shared_ptr<BIGNUM>& a,
                                   const std::shared_ptr<BIGNUM>& b);
    std::shared_ptr<BIGNUM> bn_nnmod(const std::shared_ptr<BIGNUM>& a);

    //生成消息验证码MAC使用HMAC
    std::vector<unsigned char> calculateHMAC(const std::string& key, const std::string& message);
    std::vector<unsigned char> calculateHMAC(const std::vector<unsigned char>& key_vector,
                                             const std::string& message);
    bool verifyHmac(const std::string& hmac1, const std::string& hmac2);
    std::string DERConvertTOPEM(const unsigned char* der_data, size_t der_len);
    EVP_PKEY* DER_Pubkey_Convert_TO_EVP_PKEY(const unsigned char* der_data, size_t der_len);
    //接下来需要写将EVP_PKEY提取成BIGNUM和EC_POINT类型的数据
    bool extract_Pub_Priv_Frome_EVP_PKEY(EVP_PKEY* pkey, BIGNUM** priv, EC_POINT** pub, Model model);

    //使用EVP_PKEY数据来进行数字签名
    std::string generateESDSASignature(EVP_PKEY* priKey, const std::string rawData);
    bool verifyEDSAsignature(EVP_PKEY* pubkey,
                             const std::string& rawData,
                             const std::string& signData);
    size_t computeSessionKey(EVP_PKEY* myPriKey, EVP_PKEY* peerPubKey, unsigned char** sessionKey);
    std::vector<unsigned char> SHA256(const std::vector<unsigned char>& data);

    void saveECPointToFile(const EC_POINT* point, const std::string& fileName);
    void saveBNToFile(const BIGNUM* BN, const std::string& fileName);
    void saveBNBinaryToFile(const BIGNUM* BN, const std::string& fileName);
    void saveECPOINTBinaryToFile(const EC_POINT* point, const std::string& fileName);
    BIGNUM* readBNFromFile(const std::string& fileName);
    EC_POINT* readECPOINTFromFile(const std::string& fileName);
    std::string EVP_PKEY_PublicKeyToPem(EVP_PKEY* publicKey);
    EVP_PKEY* PUBKEY_PEM_To_EVP_PKEY(const std::string& pubkeyPem);
    bool arePointsEqual(const EC_POINT* point1, const EC_POINT* point2);

private:
    void handleOpenSSLErrors() { ERR_print_errors_fp(stderr); }

private:
    BIGNUM* q_; //这是椭圆曲线的安全素数
    BIGNUM* n_; //这是椭圆曲线的子群的阶
    BIGNUM* a_;
    BIGNUM* b_;
    EC_POINT* P_;               //这是椭圆曲线的基点
    EC_GROUP* curveAttributes_; //这是椭圆曲线的相关属性
    // EC_GROUP *不是加法群的结构，而是该椭圆曲线的所以参数都保存在这个结构体中。
    BIGNUM* XP_; //这是基点和横坐标
    BIGNUM* YP_; //这是基点的纵坐标
    BIGNUM* h_;  //这是曲线的协同因子
    BN_CTX* ctx_; // OpenSSL 中的一个结构体指针，用于管理 BIGNUM 操作的临时内存空间。
    //使用q来生成q阶的椭圆曲线加法群G。
    // unsigned char *salt_;
    // unsigned char *saltHash_;
};

#endif // ECCOPERATOR_H
